package org.six11.ss.server;

import java.io.PrintWriter;
import java.util.List;
import java.util.Set;

import org.jdom.Element;
import org.six11.ss.server.bean.Game;
import org.six11.ss.server.bean.Person;
import org.six11.ss.server.bean.Room;
import org.six11.ss.shared.Message;
import org.six11.util.adt.MultiState;

/**
 * This is a logic handler dealing with room requests: listing, joining (leaving) rooms.
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 */
public class RoomHandler extends AppletMessageHandler {

  public static int ROOM_OFFLINE = 1;
  public static int ROOM_LOBBY = 2;

  public RoomHandler(MultiState state) {
    super(state);
  }

  /**
   * Responds to 'room' messages.
   */
  @SuppressWarnings("null")
  @Override
  public void handle(Message m, String sessionID, PrintWriter out) {

    Person person = getPersonBySession(sessionID);
    try {
      if (person == null) {
        log("You aren't logged in.");
        return;
      }
      Room currentRoom = person.getRoom();
      if (currentRoom == null) {
        error("You aren't in a room, which is weird.");
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    if (m.getParam("operation").equals("join")) {
      handleJoinRoom(person, m.getParam("roomName"));
    } else if (m.getParam("operation").equals("list")) {
      handleListRooms(person);
    }
  }

  public void handleJoinRoom(Person person, String desiredRoom) {
    Room nextRoom = (Room) getSession().createQuery("select r from Room as r where r.name = ?")
        .setString(0, desiredRoom).uniqueResult();

    if (nextRoom == null) {
      bug("Creating room '" + desiredRoom + "'");
      nextRoom = new Room();
      nextRoom.setName(desiredRoom);
      nextRoom.setHidden(false);
      getSession().save(nextRoom);
    }
    leaveCurrentRoom(person, "gone to " + desiredRoom);
    joinRoom(nextRoom, person);
    sendListRoomsToLobby();
  }

  public void handleListRooms(Person person) {
    Message reply = makeListRoomsMessage();
    getMessageQueue(person).add(reply);
  }

  public void sendListRoomsToLobby() {
    Room lobby = loadRoom("" + ROOM_LOBBY);
    Message list = makeListRoomsMessage();
    sendMessageToAll(list, lobby);
  }

  private Message makeListRoomsMessage() {
    List<Room> rooms = getSession().createQuery("select r from Room as r where r.hidden is ?")
        .setInteger(0, 0).list();
    Message roomListMsg = new Message("roomList");
    Element roomList = roomListMsg.getRoot();
    for (Room room : rooms) {
      if (room.getId() == ROOM_LOBBY) {
        continue; // don't show the lobby.
      }
      if (room.getPlayers().size() == 0) {
        continue; // don't show empty rooms.
      }
      Element roomElm = new Element("room");
      roomElm.setAttribute("id", "" + room.getId());
      roomElm.setAttribute("name", room.getName());
      roomElm.setAttribute("population", "" + room.getPlayers().size());
      roomList.addContent(roomElm);
    }
    return roomListMsg;
  }

  public void leaveCurrentRoom(Person person, String reason) {
    Room originalRoom = person.getRoom();
    // if the room in question is the offline room this is a no-op.
    if (originalRoom.getId() != ROOM_OFFLINE) {
      Room lobby = (Room) getSession().createQuery("select r from Room as r where r.id is ?")
          .setInteger(0, ROOM_LOBBY).uniqueResult();
      person.setRoom(lobby);
      originalRoom.getPlayers().remove(person);
      Message reply = new Message("roomLeft");
      reply.addParam("roomName", originalRoom.getName());
      reply.addParam("roomID", "" + originalRoom.getId());
      reply.addParam("personNick", person.getNick());
      reply.addParam("personID", person.getId() + "");
      if (reason != null) {
        reply.addParam("reason", reason);
      }
      getMessageQueue(person).add(reply);
      sendMessageToAll(reply.copy(), originalRoom);
      // end the game if everybody leaves.
      if (originalRoom.getCurrentGame() != null && originalRoom.getPlayers().size() < 2) {
        log("Too many people left the game " + originalRoom.getName());
        closeRoom(originalRoom);
      }
      GameHandler gh = (GameHandler) getHandler("game");
      gh.sendStatus(person); // this will tell the client to show the room list.
    }
  }

  public void joinRoom(Room room, Person person) {
    person.setRoom(room);
    room.getPlayers().add(person);
    bug("Person " + person.getNick() + " joined room " + room.getName() + ". There are now "
        + room.getPlayers().size() + " players there.");

    if (room.getId() != ROOM_OFFLINE) {
      Message reply = new Message("roomJoined");
      reply.addParam("playerNick", person.getNick());
      reply.addParam("roomName", room.getName());
      reply.addParam("roomID", "" + room.getId());

      sendMessageToAll(reply.copy(), room);
      if (room.getId() != ROOM_LOBBY && room.getPlayers().size() > 1
          && room.getCurrentGame() == null) {
        startNewGame(room);
      } else if (room.getId() != ROOM_LOBBY && room.getPlayers().size() > 1
          && room.getCurrentGame() != null) {
      }
      
      // notify newcomer who is present
      sendRoomMembership(room.getId() + "", person.getId() + "");
      
      GameHandler gh = (GameHandler) getHandler("game");
      gh.sendStatus(person);
    }
  }
  
  public void sendRoomMembership(String roomID, String personID) {
    Room room = loadRoom(roomID);
    Person person = loadPerson(personID);
    Message whoMsg = new Message("roomMembership");
    Set<Person> players = room.getPlayers();
    for (Person p : players) {
      Element elm = new Element("player");
      elm.setAttribute("name", p.getNick());
      whoMsg.addElement(elm);
    }
    getMessageQueue(person).add(whoMsg);
  }

  public void sendMessageToAll(Message msg, Room room) {
    for (Person p : room.getPlayers()) {
      try {
        getMessageQueue(p).add(msg.copy());
      } catch (NullPointerException ex) {
        // bug("error in handling msg that really wants to be of type: " + msg.getTmpType());
        if (p == null) {
          throw new RuntimeException("a player is null", ex);
        } else if (getMessageQueue(p) == null) {
          log("player " + p.getId() + "'s queue is null. May be a race condition?");
        } else {
          throw new RuntimeException("Not sure wth happened.", ex);
        }
      }
    }
  }

  protected void startNewGame(Room room) {
    // first ensure the current game is cleaned up, if there is one.
    Game currentGame = room.getCurrentGame();
    // now make a new game and get it going.
    GameHandler gh = (GameHandler) getHandler("game");
    gh.beginGame(room);
  }

  protected void closeRoom(Room room) {
    room.setCurrentGame(null);
    // Room lobby = getLobby();
    // GameHandler gh = (GameHandler) getHandler("game");
    for (Person p : room.getPlayers()) {
      leaveCurrentRoom(p, "room shut down");
    }
  }

  protected Room getLobby() {
    Room lobby = (Room) getSession().load(Room.class, ROOM_LOBBY);
    return lobby;
  }
}
